
//--Yangxin--

`include "defines.v"


module id_stage(
	input  wire           				clk           ,
	input  wire           				reset         ,
	//allowin
	input  wire           				es_allowin    ,
	output wire           				ds_allowin    ,
	//from fs
	input  wire           				fs_to_ds_valid,
	input  wire [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus  ,
	//to es
	output wire 		  				ds_to_es_valid,
	output wire [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus  ,
	//to fs
	output wire [      `BR_BUS_WD -1:0] br_bus        ,
	//forward
	input  wire [                 69:0] es_to_ds_fwd  ,
	input  wire [                 68:0] ms_to_ds_fwd  ,
	input  wire [                 68:0] ws_to_ds_fwd  ,
	//to rf: for write_back
	input  wire [`WS_TO_RF_BUS_WD -1:0] ws_to_rf_bus  ,
	output wire [                 63:0] regs_o[0: 31] , //difftest 
	//except
	input  wire 						except_flush  
	);

 wire inst_lui   ;   
 wire inst_auipc ;
 wire inst_jal   ;
 wire inst_jalr  ;
 wire inst_beq   ;
 wire inst_bne   ;
 wire inst_blt   ;
 wire inst_bge   ;
 wire inst_bltu  ;
 wire inst_bgeu  ;
 wire inst_lb    ;
 wire inst_lh    ;
 wire inst_lw    ;
 wire inst_lbu   ;
 wire inst_lhu   ;
 wire inst_sb    ;
 wire inst_sh    ;
 wire inst_sw    ;
 wire inst_addi  ;
 wire inst_slti  ;
 wire inst_sltiu ;
 wire inst_xori  ;
 wire inst_ori   ;
 wire inst_andi  ;

 wire inst_slli  ;
 wire inst_srli  ;
 wire inst_srai  ;

 wire inst_add   ;
 wire inst_sub   ;
 wire inst_sll   ;
 wire inst_slt   ;
 wire inst_sltu  ;
 wire inst_xor   ;
 wire inst_srl   ;
 wire inst_sra   ;
 wire inst_or    ;
 wire inst_and   ;

 wire inst_lwu   ;
 wire inst_ld    ;
 wire inst_sd    ;

 wire inst_addiw ;
 wire inst_slliw ;
 wire inst_srliw ; 
 wire inst_sraiw ;
 wire inst_addw  ; 
 wire inst_subw  ; 
 wire inst_sllw  ; 
 wire inst_srlw  ; 
 wire inst_sraw  ;


 //csr
 wire inst_csrrw ;
 wire inst_csrrs ;
 wire inst_csrrc ;
 wire inst_csrrwi;
 wire inst_csrrsi;
 wire inst_csrrci;

//interrupt
wire inst_ecall;
wire inst_mret;
wire ds_except_enter_ecall;
wire ds_except_quit_mret;




wire [6:0] opcode   ;
wire [4:0] rd       ;
wire [2:0] func3    ;
wire [6:0] func7    ;
wire [5:0] func7_6  ;
wire [4:0] rs1      ;
wire [4:0] rs2      ;

wire [127:0] opcode_d ;
wire [7:0] func3_d  ;
wire [127:0] func7_d  ;
wire [63:0] func7_6_d;


assign opcode  = ds_inst[ 6: 0];
assign rd      = ds_inst[11: 7];
assign func3   = ds_inst[14:12];
assign func7   = ds_inst[31:25];
assign func7_6 = ds_inst[31:26];
assign rs1     = ds_inst[19:15];
assign rs2     = ds_inst[24:20];

decoder_7_128   u_dec0 (.in(opcode), .out(opcode_d));
decoder_3_8     u_dec1 (.in(func3), .out(func3_d))  ;
decoder_7_128   u_dec2 (.in(func7), .out(func7_d))  ;
decoder_6_64     u_dec3 (.in(func7_6), .out(func7_6_d));

//decide instruction
assign inst_lui   = opcode_d[7'h37];
assign inst_auipc = opcode_d[7'h17];
assign inst_jal   = opcode_d[7'h6F];
assign inst_jalr  = opcode_d[7'h67] & func3_d[3'd0];
assign inst_beq   = opcode_d[7'h63] & func3_d[3'd0];
assign inst_bne   = opcode_d[7'h63] & func3_d[3'd1];
assign inst_blt   = opcode_d[7'h63] & func3_d[3'd4];
assign inst_bge   = opcode_d[7'h63] & func3_d[3'd5];
assign inst_bltu  = opcode_d[7'h63] & func3_d[3'd6];
assign inst_bgeu  = opcode_d[7'h63] & func3_d[3'd7];
assign inst_lb    = opcode_d[7'h03] & func3_d[3'd0];
assign inst_lh    = opcode_d[7'h03] & func3_d[3'd1];
assign inst_lw    = opcode_d[7'h03] & func3_d[3'd2];
assign inst_lbu   = opcode_d[7'h03] & func3_d[3'd4];
assign inst_lhu   = opcode_d[7'h03] & func3_d[3'd5];
assign inst_sb    = opcode_d[7'h23] & func3_d[3'd0];
assign inst_sh    = opcode_d[7'h23] & func3_d[3'd1];
assign inst_sw    = opcode_d[7'h23] & func3_d[3'd2];
assign inst_addi  = opcode_d[7'h13] & func3_d[3'd0];
assign inst_slti  = opcode_d[7'h13] & func3_d[3'd2];
assign inst_sltiu = opcode_d[7'h13] & func3_d[3'd3];
assign inst_xori  = opcode_d[7'h13] & func3_d[3'd4];
assign inst_ori   = opcode_d[7'h13] & func3_d[3'd6];
assign inst_andi  = opcode_d[7'h13] & func3_d[3'd7];

assign inst_slli  = opcode_d[7'h13] & func3_d[3'd1] && func7_6_d[6'h0];
assign inst_srli  = opcode_d[7'h13] & func3_d[3'd5] && func7_6_d[6'h0];
assign inst_srai  = opcode_d[7'h13] & func3_d[3'd5] && func7_6_d[6'h10];

assign inst_add   = opcode_d[7'h33] & func3_d[3'd0] && func7_d[7'h0 ];
assign inst_sub   = opcode_d[7'h33] & func3_d[3'd0] && func7_d[7'h20];
assign inst_sll   = opcode_d[7'h33] & func3_d[3'd1] && func7_d[7'h0 ];
assign inst_slt   = opcode_d[7'h33] & func3_d[3'd2] && func7_d[7'h0 ];
assign inst_sltu  = opcode_d[7'h33] & func3_d[3'd3] && func7_d[7'h0 ];
assign inst_xor   = opcode_d[7'h33] & func3_d[3'd4] && func7_d[7'h0 ];
assign inst_srl   = opcode_d[7'h33] & func3_d[3'd5] && func7_d[7'h0 ];
assign inst_sra   = opcode_d[7'h33] & func3_d[3'd5] && func7_d[7'h20];
assign inst_or    = opcode_d[7'h33] & func3_d[3'd6] && func7_d[7'h0 ];
assign inst_and   = opcode_d[7'h33] & func3_d[3'd7] && func7_d[7'h0 ];

assign inst_lwu   = opcode_d[7'h03] & func3_d[3'd6];
assign inst_ld    = opcode_d[7'h03] & func3_d[3'd3];
assign inst_sd    = opcode_d[7'h23] & func3_d[3'd3];

assign inst_addiw = opcode_d[7'h1B] & func3_d[3'd0];
assign inst_slliw = opcode_d[7'h1B] & func3_d[3'd1] & func7_d[7'h0 ];
assign inst_srliw = opcode_d[7'h1B] & func3_d[3'd5] & func7_d[7'h0 ];
assign inst_sraiw = opcode_d[7'h1B] & func3_d[3'd5] & func7_d[7'h20];
assign inst_addw  = opcode_d[7'h3B] & func3_d[3'd0] & func7_d[7'h0 ];
assign inst_subw  = opcode_d[7'h3B] & func3_d[3'd0] & func7_d[7'h20];
assign inst_sllw  = opcode_d[7'h3B] & func3_d[3'd1] & func7_d[7'h0 ];
assign inst_srlw  = opcode_d[7'h3B] & func3_d[3'd5] & func7_d[7'h0 ];
assign inst_sraw  = opcode_d[7'h3B] & func3_d[3'd5] & func7_d[7'h20];

//csr
assign inst_csrrw  = opcode_d[7'h73] & func3_d[3'd1];
assign inst_csrrs  = opcode_d[7'h73] & func3_d[3'd2];
assign inst_csrrc  = opcode_d[7'h73] & func3_d[3'd3];
assign inst_csrrwi = opcode_d[7'h73] & func3_d[3'd5];
assign inst_csrrsi = opcode_d[7'h73] & func3_d[3'd6];
assign inst_csrrci = opcode_d[7'h73] & func3_d[3'd7];

//interrupt
assign inst_ecall      = (ds_inst == 32'h73)      ;
assign inst_mret       = (ds_inst == 32'h30200073);
assign ds_except_enter_ecall = inst_ecall         ;
assign ds_except_quit_mret  = inst_mret                ;

reg          ds_valid   ;
wire         ds_ready_go;

wire [                 63:0] fs_pc      ;
reg  [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus_r;
assign       fs_pc = fs_to_ds_bus_r[63:0];

wire [31:0]  ds_inst    ;
wire [63:0]  ds_pc      ;
assign      {ds_inst    ,
		     ds_pc} = fs_to_ds_bus_r;

wire        rf_we       ;
wire [4:0]  rf_waddr    ;
wire [63:0] rf_wdata    ;
assign      {rf_we		,        //69
	        rf_waddr    ,        //68:64
	        rf_wdata             //63:0
	         } = ws_to_rf_bus; 

wire        br_stall    ;
wire        br_taken    ;
wire [63:0] br_target   ;

wire [15:0] alu_op      ;
wire 		load_op     ;
wire [ 5:0] inst_imm_type_sel;
wire [63:0] imm_value   ;
wire        src2_is_sa  ;
wire        src1_is_pc  ;
wire        src2_is_imm ;
wire        src2_is_4   ;   
wire        alu_res_cut ;
wire        res_from_mem;
wire        gr_we    ;
wire        mem_we   ;
wire [ 4:0] dest     ;
wire [63:0] rs1_value;  //true value form regfile or data forward
wire [63:0] rs2_value;  //true value form regfile or data forward
wire		dst_is_rd;

wire [ 4:0] rf_raddr1;
wire [63:0] rf_rdata1;
wire [ 4:0] rf_raddr2;
wire [63:0] rf_rdata2;

//csr
wire        csr_rd   ;
wire        csr_wr   ;
wire        csr_imm  ;
wire [11:0] csr_waddr;



//jump and branch
wire        rs1_eq_rs2;
wire 		rs1_lt_rs2;
wire        rs1_ltu_rs2;

//load and store
wire [10:0] mem_control;
assign mem_control = {inst_lb , inst_lh , inst_lw ,
					  inst_lbu, inst_lhu, inst_sb ,
					  inst_sh , inst_sw , inst_lwu,
					  inst_ld , inst_sd           
					  };

assign br_bus       = {br_stall,        //65:65
					   br_taken,        //64:64
					   br_target        //63:0
					  };

assign ds_to_es_bus = {ds_except_enter_ecall, //344:344
					   ds_except_quit_mret  , //343:343
					   csr_rd     ,     //342:342
                       csr_wr     ,     //341:341
					   csr_imm    ,     //340:340
					   csr_waddr  ,     //339:328
					   ds_inst    ,     //327:296
					   mem_control,     //295:285
					   alu_op     ,     //284:269
					   load_op    ,     //268:268
					   src2_is_sa ,     //267:267
					   src1_is_pc ,     //266:266
					   src2_is_imm,     //265:265
					   src2_is_4  ,     //264:264
					   alu_res_cut,     //263:263
					   gr_we      ,     //262:262
					   mem_we     ,     //261:261
					   dest       ,     //260:256
					   imm_value  ,     //255:192
					   rs1_value  ,     //191:128
					   rs2_value  ,     //127:64
					   ds_pc            //63:0
					   };


assign ds_ready_go    = ~ds_wait;
assign ds_allowin     = !ds_valid || ds_ready_go && es_allowin;
assign ds_to_es_valid = ds_valid && ds_ready_go;

always @(posedge clk) begin
	if (reset | except_flush) begin
		// reset
		ds_valid <= 1'b0;
	end
	else if (ds_allowin) begin
		ds_valid <= fs_to_ds_valid;
	end

	if(fs_to_ds_valid && ds_allowin) begin
		fs_to_ds_bus_r <= fs_to_ds_bus;
	end
end

//alu_op
assign alu_op[ 0] = inst_auipc | inst_jal  | inst_jalr  | inst_lb   | inst_lh  | inst_lw | inst_lbu 
                  | inst_lhu   | inst_lwu  | inst_ld    | inst_addi | inst_add | inst_sb | inst_sh 
                  | inst_sw    | inst_sd   | inst_addiw | inst_addw;

assign alu_op[ 1] = inst_sub   | inst_subw                             ;
assign alu_op[ 2] = inst_and   | inst_andi   						   ;

assign alu_op[ 3] = inst_or    | inst_ori  | inst_csrrs | inst_csrrsi  ;
assign alu_op[ 4] = inst_xor   | inst_xori               			   ;
assign alu_op[ 5] = inst_sll   | inst_slli | inst_slliw                ;
assign alu_op[ 6] = inst_srl   | inst_srli                             ;
assign alu_op[ 7] = inst_sra   | inst_srai                             ;
assign alu_op[ 8] = inst_slt   | inst_slti                             ;
assign alu_op[ 9] = inst_sltu  | inst_sltiu                            ;
assign alu_op[10] = inst_lui                                           ;
assign alu_op[11] = inst_sraiw | inst_sraw                             ;
assign alu_op[12] = inst_sllw                                          ;
assign alu_op[13] = inst_srlw  | inst_srliw                            ;

assign alu_op[14] = inst_csrrc | inst_csrrci                           ;   //qufan zai yu
assign alu_op[15] = inst_csrrw | inst_csrrwi                           ;   //src2 = 0;

assign load_op = inst_lw | inst_lb | inst_lh | inst_lbu | inst_lhu | inst_lwu | inst_ld;


//Immediate value
assign inst_imm_type_sel[0] = inst_jalr | inst_lb    | inst_lh    | inst_lw   | inst_lbu  | inst_lhu  | 
							  inst_addi | inst_slti  | inst_sltiu | inst_xori | inst_ori  | inst_andi |
							  inst_lwu  | inst_ld    | inst_addiw | inst_slli | inst_srli | inst_srai | 
							  inst_slliw| inst_srliw | inst_sraiw                                       ;
assign inst_imm_type_sel[1] = inst_lui  | inst_auipc                                                    ;
assign inst_imm_type_sel[2] = inst_beq  | inst_bne   | inst_blt   | inst_bge | inst_bltu | inst_bgeu    ;
assign inst_imm_type_sel[3] = inst_sb   | inst_sh    | inst_sw    | inst_sd                             ;
assign inst_imm_type_sel[4] = inst_jal                                                                  ;

Imm_Extractor Imm_Extractor_0(.instruction(ds_inst), .inst_imm_type_sel(inst_imm_type_sel), .value(imm_value));



// assign ds_to_es_bus = {alu_op      ,  //135:124
//                        load_op     ,  //123:123
//                        src1_is_sa  ,  //122:122
//                        src1_is_pc  ,  //121:121
//                        src2_is_imm ,  //120:120
//                        src2_is_8   ,  //119:119
//                        gr_we       ,  //118:118
//                        mem_we      ,  //117:117
//                        dest        ,  //116:112
//                        imm         ,  //111:96
//                        rs_value    ,  //95 :64
//                        rt_value    ,  //63 :32
//                        ds_pc          //31 :0
//                       };

assign src1_is_pc  = inst_auipc | inst_jal  | inst_jalr;
assign src2_is_imm = inst_auipc | inst_lui  | inst_lb  | inst_lh   | inst_lw   | inst_lbu   | 
					 inst_lhu   | inst_lwu  | inst_ld  | inst_addi | inst_slti | inst_addiw |
					 inst_sltiu | inst_xori | inst_ori | inst_andi | inst_sb   | 
					 inst_sh    | inst_sw   | inst_sd                                        ;
assign src2_is_4   = inst_jal   | inst_jalr                                                  ;

assign src2_is_sa  = inst_slli  | inst_srli  | inst_srai  | inst_slliw | 
                     inst_srliw | inst_sraiw                                                 ;
assign alu_res_cut = inst_addiw | inst_slliw | inst_srliw | inst_addw  | inst_subw           ;


assign res_from_mem = inst_lb | inst_lh | inst_lw | inst_lbu | inst_lhu | inst_lwu | inst_ld ;

assign dst_is_rd = 1'b1;
assign gr_we = ~inst_beq  & ~inst_bne & ~inst_blt & ~inst_bge & ~inst_bltu & ~inst_bgeu 
	         & ~inst_sb   & ~inst_sw  & ~inst_sh  & ~inst_sd;

assign mem_we = inst_sb | inst_sh | inst_sw | inst_sd;

wire inst_no_dest;
assign inst_no_dest = inst_beq | inst_bne | inst_blt | inst_bge;
assign dest = rd;

//csr
assign csr_rd  = inst_csrrw | inst_csrrs | inst_csrrc | inst_csrrwi | inst_csrrsi | inst_csrrci;
assign csr_wr  = csr_rd;
assign csr_imm = inst_csrrwi | inst_csrrsi | inst_csrrci;
assign csr_waddr = ds_inst[31:20];
//data forward


//load_stall
wire ds_wait;
wire load_stall;
assign ds_wait    = (es_to_ds_fwd[69] && (rs1 == es_to_ds_fwd[68:64]) && (rs1 != 5'h0)) || 
					(es_to_ds_fwd[69] && (rs2 == es_to_ds_fwd[68:64]) && (rs2 != 5'h0));

/************************************/
//to do 

//to do 
//to do 

//to do   2021.09.15 23:06




//assign load_stall = (rs1 == EXE_dest && es_load_op) || (rs2 == EXE_dest && es_load_op);
/************************************/
//br_stall
assign br_stall = br_taken & {ds_valid};


assign  rf_raddr1 = rs1;
assign  rf_raddr2 = rs2;

regfile inst_RegFile
	(
		.clk    (clk      ),
		.raddr1 (rf_raddr1),
		.rdata1 (rf_rdata1),
		.raddr2 (rf_raddr2),
		.rdata2 (rf_rdata2),
		.we     (rf_we    ),
		.waddr  (rf_waddr ),
		.wdata  (rf_wdata ),
		.regs_o (regs_o   )     //difftest
	);


assign  rs1_value = (es_to_ds_fwd[68:64] == rs1) && (rs1 != 5'd0) ? es_to_ds_fwd[63:0] :
				    (ms_to_ds_fwd[68:64] == rs1) && (rs1 != 5'd0) ? ms_to_ds_fwd[63:0] :
				    (ws_to_ds_fwd[68:64] == rs1) && (rs1 != 5'd0) ? ws_to_ds_fwd[63:0] :
				   								                              rf_rdata1;

assign  rs2_value = (es_to_ds_fwd[68:64] == rs2) && (rs2 != 5'd0) ? es_to_ds_fwd[63:0] :
				    (ms_to_ds_fwd[68:64] == rs2) && (rs2 != 5'd0) ? ms_to_ds_fwd[63:0] :
				    (ws_to_ds_fwd[68:64] == rs2) && (rs2 != 5'd0) ? ws_to_ds_fwd[63:0] :
				   															  rf_rdata2;

wire signed[63:0] rs1_value_signed = $signed(rs1_value);
wire signed[63:0] rs2_value_signed = $signed(rs2_value);

//JUMP and BRANCH
assign rs1_eq_rs2  = (rs1_value == rs2_value);
//assign rs1_lt_rs2  = (rs1_value_signed[63] & ~rs2_value_signed[63]) | (rs1_value_signed < rs2_value_signed );
assign rs1_lt_rs2  = (rs1_value_signed[63] & ~rs2_value_signed[63]) | (rs1_value_signed < rs2_value_signed );
assign rs1_ltu_rs2 = (rs1_value < rs2_value);

assign br_taken = ((inst_beq && rs1_eq_rs2)
			   || (inst_bne && !rs1_eq_rs2)
			   || (inst_blt &&  rs1_lt_rs2)
			   || (inst_bge && !rs1_lt_rs2)
			   || (inst_bltu && rs1_ltu_rs2)
			   || (inst_bgeu && !rs1_ltu_rs2)
			   || inst_jal
			   || inst_jalr) && ds_valid;

assign br_target = (inst_beq || inst_bne || inst_blt || inst_bge || inst_bltu || inst_bgeu) ? (fs_pc + imm_value) :
				   (inst_jal ) ? (fs_pc + imm_value) :
				   /*(inst_jalr) ?*/ (rs1_value + {imm_value[63:1],1'b0});


endmodule

/************************************************/

module decoder_3_8(
	input  wire [2:0] in,
	output wire [7:0] out
	);

genvar i;
generate for(i=0; i<8; i=i+1) begin : gen_for_dec_3_8
	assign out[i] = (in == i);
end endgenerate
endmodule



module decoder_5_32(
	input  wire [ 4:0] in,
	output wire [31:0] out
	);

genvar i;
generate for(i=0; i<32; i=i+1) begin : gen_for_dec_5_32
	assign out[i] = (in == i);
end endgenerate
endmodule




module decoder_6_64(
	input  wire [ 5:0] in,
	output wire [63:0] out
	);

genvar i;
generate for(i=0; i<64; i=i+1) begin : gen_for_dec_6_64
	assign out[i] = (in == i);
end endgenerate
endmodule




module decoder_7_128(
	input  wire [  6:0] in,
	output wire [127:0] out
	);

genvar i;
generate for(i=0; i<128; i=i+1) begin : gen_for_dec_7_128
	assign out[i] = (in == i);
end endgenerate
endmodule